﻿using MelonLoader;
using MelonLoader.Utils;
using Newtonsoft.Json.Linq;
using static SultanModLoader.ModLoadInfo;

namespace SultanModLoader
{
    
    public class ModLoader    
    {
        /// <summary>
        /// Mod配置文件名称
        /// </summary>
        public static readonly string CONFIG_FILE_NAME = "mod.json";
        /// <summary>
        /// 混合Json存放文件夹名称
        /// </summary>
        public static readonly string MODULE_FOLDER_NAME = "module";
        /// <summary>
        /// 游戏资源备份路径
        /// </summary>
        public static readonly string BAK_DIR = Path.Combine(MelonEnvironment.UnityGameDataDirectory, "StreamingAssetsBak");
        /// <summary>
        /// 游戏资源路径
        /// </summary>
        public static readonly string ASSETS_DIR = Path.Combine(MelonEnvironment.UnityGameDataDirectory, "StreamingAssets");
        /// <summary>
        /// Mod搜索路径
        /// </summary>
        public static readonly string MODS_DIR = MelonEnvironment.ModsDirectory;
        /// <summary>
        /// Mod加载配置文件路径
        /// </summary>
        public static readonly string LOADER_JSON_DIR = Path.Combine(MODS_DIR, "load.json");
        /// <summary>
        /// 文件变更列表路径
        /// </summary>
        public static readonly string CHANGED_FILE_LIST_DIR = Path.Combine(ASSETS_DIR, ".changed");

        public static readonly string EXTRA_OUTPUT_DIR = Path.Combine(MODS_DIR, "extra");

        


        public static void TryLoadMods()
        {
            // 首先尝试备份
            TryBakAssets();
            // 尝试获取loadJson文件
            var loadInfo = ReadLoadInfo();
            //if (!CheckEnable(loadInfo)) return; // 没有开启则直接退出
            if (!loadInfo.Enable) return;
            // 扫描Mod
            ScanModsConfig(loadInfo);
            // 保存Mod配置
            SaveLoadInfo(loadInfo);
            // 扫描Json文件
            ScanJsonFiles(loadInfo);
            // 扫描JToken
            ScanJTokens(loadInfo, color:ConsoleColor.Magenta);
            // 应用Mod修改
            ApplyMods(loadInfo, color:ConsoleColor.Cyan);
            // 保存修改后的文件
            OutputAssetsJson(loadInfo, color:ConsoleColor.Green);
            // 判断如果需要额外输出，则进行额外输出
            if(loadInfo.ExtraOutput) OutputExtraJson(loadInfo, color:ConsoleColor.Blue);

            //UpdateAllJson(loadInfo, modDict);
        }

        private static void TryBakAssets()
        {
            MelonLogger.WriteLine();
            // 检查备份文件夹是否存在，若不存在则创建并复制
            if (!Directory.Exists(BAK_DIR))
            {
                MelonLogger.Msg("备份不存在，创建备份");
                Directory.CreateDirectory(BAK_DIR);
                AssetsTool.CopyDirectory(ASSETS_DIR, BAK_DIR);
                MelonLogger.Msg($"备份完成: {BAK_DIR}");
            }
            Directory.Delete(ASSETS_DIR, recursive: true);
            AssetsTool.CopyDirectory(BAK_DIR, ASSETS_DIR);
        }

        private static ModLoadInfo ReadLoadInfo()
        {
            if (!File.Exists(LOADER_JSON_DIR)) return new ModLoadInfo();
            // 读取文件
            var json = File.ReadAllText(LOADER_JSON_DIR);
            // 清洗json文件
            JObject jsonObj = JObject.Parse(AssetsTool.CleanJson(json));
            // 从json中恢复LoadInfo对象
            var info = jsonObj.ToObject<ModLoadInfo>();
            if (info == null) return new ModLoadInfo();
            // 读取已改变的文件列表
            if (!File.Exists(CHANGED_FILE_LIST_DIR)) return info;
            info.ChangedFileList = File.ReadAllLines(CHANGED_FILE_LIST_DIR).ToList();
            return info;
        }

        private static void ScanModsConfig(ModLoadInfo loadInfo)
        {

            MelonLogger.WriteLine();
            MelonLogger.Msg("扫描mod ...");

            // 获取所有包含`mod.json`的文件夹
            var modFolders = Directory.GetDirectories(MODS_DIR)
                   .Where(dir => File.Exists(Path.Combine(dir, CONFIG_FILE_NAME)))
                   .ToList();

            // 遍历mod文件夹,读取mod.json配置文件
            Dictionary<string, ModInfo> modDict = new Dictionary<string, ModInfo>();
            foreach (var folder in modFolders)
            {
                // 构建配置文件路径
                var modDir = Path.Combine(MODS_DIR, folder);
                var configPath = Path.Combine(MODS_DIR, folder, CONFIG_FILE_NAME);
                MelonLogger.Msg($"读取mod配置文件：{configPath}");
                // 配置信息
                var cfgJson = AssetsTool.ParseJsonFile(configPath);
                if (cfgJson == null)
                {
                    MelonLogger.Error($"配置文件不存在:{configPath}");
                    continue;
                }
                // 根据配置文件构建ModInfo对象
                var modInfo = cfgJson.ToObject<ModInfo>();
                // 配置信息
                modInfo.ModDirectory = modDir;
                modDict[modInfo.ModKey] = modInfo;
                if(modInfo.ModuleToggle != null && modInfo.ModuleToggle.Count > 0)
                {
                    // 将所有的路径统一格式
                    modInfo.ModuleToggle = modInfo.ModuleToggle.ToDictionary(
                        kv => AssetsTool.UnifyPathSeparator(kv.Key),
                        kv => kv.Value
                    );
                }
                MelonLogger.Msg(ConsoleColor.Magenta, $"发现Mod: <{modInfo.ModKey}>");
            }
            MelonLogger.Msg(ConsoleColor.Cyan, $"共找到 {modFolders.Count} 个包含 {CONFIG_FILE_NAME} 的 Mod");


            // 根据loadinfo过滤掉未启用的mod，并生成按顺序加载的ModInfo列表
            HashSet<string> modKeys = modDict.Keys.ToHashSet();
            var newList = new List<ModConfig>();
            // 首先过滤出在modDict中有的ModConfig
            newList = loadInfo.ModLoadList
                .Where(config => modKeys.Remove(config.Key))
                .ToList();
            // 将新发现的mod也添加到ModLoadList中
            newList.AddRange(modKeys.Select(modKey => new ModConfig() { Enable = true, Key = modKey }));
            loadInfo.ModLoadList = newList;
            // 构建ModInfoList
            loadInfo.ModInfoList = newList.Where(config => config.Enable).Select(config => modDict[config.Key]).ToList();

        }


        private static void SaveLoadInfo(ModLoadInfo info)
        {
            AssetsTool.LevelMsg($"向 load.json中写入mod列表");
            foreach (var cfg in info.ModLoadList)
            {
                AssetsTool.LevelMsg($"{cfg.Key}  {cfg.Enable}", 1);
            }
            var json = JObject.FromObject(info);
            File.WriteAllText(LOADER_JSON_DIR, json.ToString());
            if (Directory.Exists(ASSETS_DIR)) File.WriteAllLines(CHANGED_FILE_LIST_DIR, info.NeedUpdateFiles.Keys);
            
        }

        private static void ScanJsonFiles(ModLoadInfo loadInfo, ConsoleColor color = ConsoleColor.Green)
        {
            MelonLogger.WriteLine();
            MelonLogger.Msg(color, "扫描Json文件");
            // 遍历所有启用的Mod
            foreach (var modInfo in loadInfo.ModInfoList)
            {
                AssetsTool.LevelMsg($"扫描Mod: {modInfo.ModKey}  Path:{modInfo.ModDirectory}", 0, color);
                // 搜索所有json文件
                modInfo.JsonFilePathList = Directory.GetFiles(modInfo.ModDirectory, "*.json", SearchOption.AllDirectories)  // 递归搜索*.json文件
                    .Select(filePath => Path.GetRelativePath(modInfo.ModDirectory, filePath))                       // 将绝对路径转为相对路径
                    .Where(path => !path.Equals(CONFIG_FILE_NAME, StringComparison.OrdinalIgnoreCase))              // 排除配置文件
                    .ToList();
                // 打印文件列表
                foreach (var jsonFilePath in modInfo.JsonFilePathList)
                {
                    if (modInfo.ModuleToggle.ContainsKey(jsonFilePath))
                    {
                        AssetsTool.LevelMsg($"{jsonFilePath}    Enable:{modInfo.ModuleToggle[jsonFilePath]}", 2, color);
                    }
                    else
                    {
                        AssetsTool.LevelMsg($"{jsonFilePath}", 2, color);
                    }
                       
                }
            }
        }


        private static void ScanJTokens(ModLoadInfo loadInfo, ConsoleColor color = ConsoleColor.DarkBlue)
        {
            AssetsTool.LevelMsg("扫描JToken");
            // 遍历所有mod
            foreach (var modInfo in loadInfo.ModInfoList)
            {
                AssetsTool.LevelMsg(modInfo.ModKey, 0, color);
                // 遍历所有文件
                foreach (var jsonPath in modInfo.JsonFilePathList)
                {
                    // 判断文件是否跳过
                    if(modInfo.ModuleToggle.ContainsKey(jsonPath) && modInfo.ModuleToggle[jsonPath] == false)
                    {
                        AssetsTool.LevelMsg($"{jsonPath}    Module已禁用", 1, color);
                        continue;
                    }

                    AssetsTool.LevelMsg($"扫描文件:{jsonPath}", 1, color);
                    // 处理标准文件
                    JObject jObject = AssetsTool.ParseJsonFile(Path.Combine(modInfo.ModDirectory, jsonPath));
                    //MelonLogger.Msg(jObject.ToString());
                    // 不是模块，按常规处理
                    if (!jsonPath.StartsWith(MODULE_FOLDER_NAME))
                    {
                        ScanJTokens(jsonPath, jObject, modInfo, color: color, msgLevel: 2);
                        continue;
                    }
                    // 读取到模块文件
                    foreach (var prop in jObject.Properties())
                    {
                        var targetPath = prop.Name;
                        // 判断属性名是否为*.json
                        if (!targetPath.ToLower().EndsWith(".json")) continue;
                        if (jObject[targetPath].Type != JTokenType.Object)
                        {
                            AssetsTool.LevelMsg($"模块内Key:{targetPath}对应的Value的格式不正确", 2,ConsoleColor.Red);
                            continue;
                        }
                        //AssetsTool.LevelMsg($" ---- {jObject[prop.Name]}");
                        ScanJTokens(prop.Name, (JObject)jObject[targetPath], modInfo, sourceJsonPath: jsonPath, color: color, msgLevel: 2);
                    }


                }
            }
        }
        private static void ScanJTokens(string targetJsonPath, JObject jObject, ModInfo modInfo, string sourceJsonPath = "", ConsoleColor color = ConsoleColor.White, int msgLevel = 0)
        {
            sourceJsonPath = string.IsNullOrEmpty(sourceJsonPath) ? targetJsonPath : sourceJsonPath;
            // 统一sourceJsonPath和targetJsonPath的分隔符格式
            targetJsonPath = AssetsTool.UnifyPathSeparator(targetJsonPath);
            sourceJsonPath = AssetsTool.UnifyPathSeparator(sourceJsonPath);
            if (jObject == null) return;
            foreach (var option in ModJsonOption.Options)
            {
                if (!jObject.ContainsKey(option)) continue;
                // 获取缓存Dict
                var jTokenDictByOption = modInfo.JTokenDictsByOption[option];
                // 判断对应文件Path的Key - value是否存在，若不存在则创建
                if (!jTokenDictByOption.ContainsKey(targetJsonPath) || jTokenDictByOption[targetJsonPath] == null) jTokenDictByOption[targetJsonPath] = new List<(JToken, string)>();
                // 判断如果源文件不存在，则使用目标文件
                AssetsTool.LevelMsg($"{sourceJsonPath}[{option}] --> {targetJsonPath}", msgLevel, color);
                jTokenDictByOption[targetJsonPath].Add((jObject[option], sourceJsonPath));

            }
        }


        private static void ApplyMods(ModLoadInfo loadInfo, ConsoleColor color = ConsoleColor.Cyan)
        {
            MelonLogger.WriteLine();
            AssetsTool.LevelMsg("应用Mod");
            // 遍历mod
            foreach (var modInfo in loadInfo.ModInfoList)
            {
                AssetsTool.LevelMsg($"{modInfo.ModKey}", 0, color);
                // 遍历操作
                foreach (var option in ModJsonOption.Options)
                {
                    if (!modInfo.JTokenDictsByOption.ContainsKey(option)) continue;
                    var optionDict = modInfo.JTokenDictsByOption[option];
                    if (optionDict.Count > 0) AssetsTool.LevelMsg($"{option}", 1, color);
                    // 遍历需要修改的文件
                    foreach (var kv in optionDict)
                    {

                        var targetFile = kv.Key;
                        var targetFilePath = Path.Combine(ASSETS_DIR, targetFile);
                        if (kv.Value != null && kv.Value.Count > 0) AssetsTool.LevelMsg($"target: {targetFile}", 2, color);
                        // 判断loadInfo中是否缓存了对应文件的JObject
                        if (!loadInfo.NeedUpdateFiles.ContainsKey(targetFile))
                        {
                            var parse = AssetsTool.ParseJsonFile(targetFilePath);
                            loadInfo.NeedUpdateFiles[targetFile] = (parse == null) ? new JObject() : parse;
                        }
                        var targetJObject = loadInfo.NeedUpdateFiles[targetFile];
                        // 遍历所有修改项
                        foreach (var (jToken, sourceFile) in kv.Value)
                        {
                            if (jToken.Children().Count() == 0) continue;
                            AssetsTool.LevelMsg($"source: {sourceFile}  {option}", 3, color);
                            switch (option)
                            {
                                case ModJsonOption.Create:
                                    ModJsonOption.ApplyCreate(targetJObject, jToken, ConsoleColor.Yellow, 4);
                                    break;
                                case ModJsonOption.Replace:
                                    ModJsonOption.ApplyReplace(targetJObject, jToken, ConsoleColor.Yellow, 4);
                                    break;
                                case ModJsonOption.Merge:
                                    ModJsonOption.ApplyMerge(targetJObject, jToken, ConsoleColor.Yellow, 4);
                                    break;

                            }
                        }
                    }
                }
            }
        }



        private static void OutputAssetsJson(ModLoadInfo loadInfo, ConsoleColor color = ConsoleColor.White)
        {
            MelonLogger.WriteLine();
            MelonLogger.Msg("回写游戏文件", color);
            SaveJson(loadInfo, ASSETS_DIR, reCreateDir: false, color: color);
            AssetsTool.LevelMsg($"回写文件 Count:{loadInfo.NeedUpdateFiles.Count}");
        }

        private static void OutputExtraJson(ModLoadInfo loadInfo, ConsoleColor color = ConsoleColor.White)
        {
            MelonLogger.WriteLine();
            MelonLogger.Msg("额外输出文件", color);
            SaveJson(loadInfo, EXTRA_OUTPUT_DIR, reCreateDir: true, color: color);
            AssetsTool.LevelMsg($"额外输出文件 Count:{loadInfo.NeedUpdateFiles.Count}");
        }


        private static void SaveJson(ModLoadInfo loadInfo, string baseDir, bool reCreateDir = false, ConsoleColor color = ConsoleColor.White, int msgLevel = 1)
        {
            
            if (reCreateDir)
            {
                if (Directory.Exists(baseDir)) Directory.Delete(baseDir, recursive:true);
                Directory.CreateDirectory(baseDir);
            }
            foreach (var kv in loadInfo.NeedUpdateFiles)
            {
                var fileName = kv.Key;
                var json = kv.Value;
                var filePath = Path.Combine(baseDir, fileName);
                var dirPath = Path.GetDirectoryName(filePath);
                if (!Directory.Exists(dirPath)) Directory.CreateDirectory(dirPath);
                AssetsTool.LevelMsg($"{filePath}", level:msgLevel, color:color);
                File.WriteAllText(filePath, json.ToString());
            }
            
        }
        
    }
}